Skip to content

feat(xpkg): add --annotation flag to xpkg build and xpkg push#11

Open
chaitanyapantheor wants to merge 5 commits into
crossplane:mainfrom
chaitanyapantheor:issues/7282
Open

feat(xpkg): add --annotation flag to xpkg build and xpkg push#11
chaitanyapantheor wants to merge 5 commits into
crossplane:mainfrom
chaitanyapantheor:issues/7282

Conversation

@chaitanyapantheor
Copy link
Copy Markdown

Description of your changes

Adds a repeatable --annotation/-a KEY=VALUE flag to crossplane xpkg build
and crossplane xpkg push, allowing users to attach OCI manifest annotations
to packages at build or push time.

# Build with a static annotation
crossplane xpkg build -a org.opencontainers.image.source=https://github.com/org/repo

# Push with a dynamic (CI-injected) annotation
crossplane xpkg push -f pkg.xpkg \
  -a org.opencontainers.image.revision=$(git rev-parse HEAD) \
  xpkg.example.io/org/pkg:v1.0.0

Annotations are applied to the OCI image manifest. Malformed annotations
(missing =) return an error before any write or push occurs. Reading
annotations from crossplane.yaml is intentionally out of scope to avoid
silently propagating internal Crossplane annotations to the OCI registry.

Covered by unit tests in cmd/crossplane/xpkg/annotations_test.go.

Fixes crossplane/crossplane#7282

I have:

  • Read and followed Crossplane's [contribution process].
  • Run ./nix.sh flake check to ensure this PR is ready for review.
  • Added or updated unit tests.
  • Linked a PR or a [docs tracking issue] to [document this change].
  • Added backport release-x.y labels to auto-backport this PR.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 12, 2026

Review Change Stack

📝 Walkthrough

Walkthrough

Adds repeatable --annotation/-a flags to xpkg build and xpkg push, parses KEY=VALUE entries into a map, and applies those annotations to OCI image and index manifests during build and push flows.

Changes

OCI Annotation CLI Support

Layer / File(s) Summary
Annotation parsing and image/index application helpers
cmd/crossplane/xpkg/annotations.go, cmd/crossplane/xpkg/annotations_test.go
parseAnnotations parses repeatable KEY=VALUE CLI args into map[string]string with erroring on malformed entries; annotateImage and annotateIndex apply non-empty annotations via mutate.Annotations. Tests cover empty input, multiple entries, values containing =, and missing delimiters.
Build command annotation support
cmd/crossplane/xpkg/build.go
Adds --annotation/-a flag to buildCmd, introduces errParseAnnotations, parses annotations in Run(), and applies annotations to the built image before digest calculation and packaging.
Push command annotation support
cmd/crossplane/xpkg/push.go
Adds --annotation/-a flag to pushCmd, parses annotations and passes them into pushImages (signature updated). Applies annotateImage after xpkg.AnnotateLayers for single- and multi-package pushes and uses annotateIndex when writing an index.
Batch retry wiring
cmd/crossplane/xpkg/batch.go
Updates the retry loop's pushImages invocation to include the new annotations parameter (passed as nil at this callsite).

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related issues

🚥 Pre-merge checks | ✅ 6
✅ Passed checks (6 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and descriptively summarizes the main change: adding annotation flags to build and push commands, is 61 characters (under 72), uses proper conventional commit format, and directly relates to the changeset.
Description check ✅ Passed The description is well-detailed, directly related to the changes, includes practical examples, references the linked issue, and explains design decisions like intentionally excluding crossplane.yaml annotations.
Linked Issues check ✅ Passed The PR fully addresses issue #7282 by implementing option (1) from the issue: adding a repeatable --annotation/-a flag to both xpkg build and xpkg push commands, enabling dynamic OCI manifest annotations.
Out of Scope Changes check ✅ Passed All changes are scoped to implementing the --annotation flag feature: annotation parsing/application helpers, flag additions to build and push commands, and comprehensive unit tests. No unrelated changes detected.
Breaking Changes ✅ Passed No breaking changes detected. Only new optional flags added (--oci-annotation) and internal unexported functions modified. No public fields/flags removed, renamed, or made required.
Feature Gate Requirement ✅ Passed This PR adds CLI flags to crossplane xpkg build/push commands. No apis/** affected. Not marked experimental. Feature gates typically apply to core API behavior, not CLI tools.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
⚔️ Resolve merge conflicts
  • Resolve merge conflict in branch issues/7282

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Member

@adamwg adamwg left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've left a couple of thoughts, but overall this looks good to me and seems like a valuable feature.

Comment thread cmd/crossplane/xpkg/push.go Outdated
Package string `arg:"" help:"Where to push the package. Must be a fully qualified OCI tag, including the registry, repository, and tag." placeholder:"REGISTRY/REPOSITORY:TAG"`

// Flags. Keep sorted alphabetically.
Annotation []string `help:"An OCI manifest annotation to add to the package in key=value format. Repeatable." placeholder:"KEY=VALUE" short:"a"`
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if this should be OCIAnnotation / --oci-annotation or something like that to make it clear how it's different from the metadata.annotations.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point — renamed to --oci-annotation (Go field OCIAnnotation, explicit name:"oci-annotation" kong tag) in both xpkg build and xpkg push. Short flag -a is unchanged.

return errors.Wrapf(err, errAnnotateLayers)
}

img = annotateImage(img, annotations)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we apply the same annotations to the index (built below with mutate.AppendManifests)?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added annotateIndex helper (mirrors annotateImage) and applied it to the index before remote.WriteIndex in the multi-platform path. Annotations now appear on both the individual manifests and the top-level index.

Comment thread cmd/crossplane/xpkg/annotations.go Outdated
@@ -0,0 +1,49 @@
/*
Copyright 2025 The Crossplane Authors.
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: 2026 :-)

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed — updated to 2026.

- Rename --annotation to --oci-annotation in xpkg build and push to
  distinguish from Kubernetes metadata.annotations
- Apply OCI annotations to the image index in the multi-platform push
  path, not only to individual manifests
- Fix copyright year in annotations.go and annotations_test.go

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
@chaitanyapantheor chaitanyapantheor requested a review from adamwg June 6, 2026 18:25
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
cmd/crossplane/xpkg/annotations.go (1)

30-40: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Consider validating that annotation keys are non-empty.

The current implementation would accept an annotation like =value (empty key), which could cause confusing errors later when pushing to the registry. Adding a simple validation would provide users with a clearer, earlier error message.

🛡️ Suggested validation
 func parseAnnotations(kvs []string) (map[string]string, error) {
 	anns := make(map[string]string, len(kvs))
 	for _, kv := range kvs {
 		k, v, ok := strings.Cut(kv, "=")
 		if !ok {
 			return nil, errors.Errorf("invalid annotation %q: must be in key=value format", kv)
 		}
+		if k == "" {
+			return nil, errors.Errorf("invalid annotation %q: key cannot be empty", kv)
+		}
 		anns[k] = v
 	}
 	return anns, nil
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@cmd/crossplane/xpkg/annotations.go` around lines 30 - 40, The
parseAnnotations function accepts "k=v" pairs but doesn't reject empty keys
(e.g., "=value"); update parseAnnotations to validate that the extracted key
(variable k from strings.Cut on each kv) is non-empty and, if empty, return a
clear formatted error (similar to the existing errors.Errorf) indicating the
annotation has an empty key; ensure this check happens after the strings.Cut ok
check and before assigning into anns so no empty-key entries are inserted.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@cmd/crossplane/xpkg/annotations.go`:
- Around line 30-40: The parseAnnotations function accepts "k=v" pairs but
doesn't reject empty keys (e.g., "=value"); update parseAnnotations to validate
that the extracted key (variable k from strings.Cut on each kv) is non-empty
and, if empty, return a clear formatted error (similar to the existing
errors.Errorf) indicating the annotation has an empty key; ensure this check
happens after the strings.Cut ok check and before assigning into anns so no
empty-key entries are inserted.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 0477f7aa-6ce3-4b3c-953a-d9cd84c3a305

📥 Commits

Reviewing files that changed from the base of the PR and between a8a515b and c9e7a70.

📒 Files selected for processing (4)
  • cmd/crossplane/xpkg/annotations.go
  • cmd/crossplane/xpkg/annotations_test.go
  • cmd/crossplane/xpkg/build.go
  • cmd/crossplane/xpkg/push.go
✅ Files skipped from review due to trivial changes (1)
  • cmd/crossplane/xpkg/annotations_test.go
🚧 Files skipped from review as they are similar to previous changes (2)
  • cmd/crossplane/xpkg/build.go
  • cmd/crossplane/xpkg/push.go

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Setting OCI Annotations on artifacts produced from crossplane xkpg build/push commands

2 participants